package com.neusoft.databus.core.service.impl;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.querydsl.QPageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.excel.poi.ExcelBoot;
import com.excel.poi.function.ExportFunction;
import com.google.common.eventbus.AsyncEventBus;
import com.neusoft.bizcore.common.utils.DateUtil;
import com.neusoft.bizcore.web.exception.CustomRuntimeException;
import com.neusoft.bizcore.web.support.Searchable;
import com.neusoft.databus.common.constant.DatabusErrorCode;
import com.neusoft.databus.common.dto.MetricGroupDTO;
import com.neusoft.databus.common.model.Metric;
import com.neusoft.databus.common.model.MetricGroup;
import com.neusoft.databus.core.constant.DatabusCoreConstant;
import com.neusoft.databus.core.converter.MetricGroupConverter;
import com.neusoft.databus.core.repository.MetricGroupRepository;
import com.neusoft.databus.core.repository.MetricRepository;
import com.neusoft.databus.core.service.MetricGroupService;
import com.neusoft.databus.core.vo.MetricGroupPortVO;

/**
 * @author naxiang
 */
@Service
@Transactional
public class MetricGroupServiceImpl implements MetricGroupService {
    @Autowired
    private MetricGroupConverter metricGroupConverter;
    @Autowired
    private MetricGroupRepository metricGroupRepository;
    @Autowired
    private MetricRepository metricRepository;
    @javax.annotation.Resource(name = "modelUpdateEventBus")
    private AsyncEventBus modelUpdateEventBus;

    @PostConstruct
    public void init() {
        this.modelUpdateEventBus.register(this);
    }

    @Override
    public MetricGroupDTO findById(final Long id) {
        final MetricGroup model = this.metricGroupRepository.getOne(id);
        if (null == model) {
            throw new CustomRuntimeException(DatabusErrorCode.ERR_CODE_404001, new Object[] {id });
        }
        return this.metricGroupConverter.toDTO(model);
    }

    @Override
    public MetricGroupDTO findByCode(final String code) {
        final MetricGroup model = this.metricGroupRepository.findByCode(code);
        if (null == model) {
            throw new CustomRuntimeException(DatabusErrorCode.ERR_CODE_404001, new Object[] {code });
        }
        return this.metricGroupConverter.toDTO(model);
    }

    @Override
    public List<MetricGroupDTO> findAll() {
        final List<MetricGroup> models = this.metricGroupRepository.findAll();
        return this.metricGroupConverter.toListDTO(models);
    }

    @Override
    public Page<MetricGroupDTO> search(Pageable pageable, Searchable searchable) {
        final Page<MetricGroup> models = this.metricGroupRepository.search(pageable, searchable);
        return this.metricGroupConverter.toPageDTO(models);
    }

    @Override
    public List<MetricGroupDTO> search(Searchable searchable) {
        final List<MetricGroup> models = this.metricGroupRepository.search(searchable);
        return this.metricGroupConverter.toListDTO(models);
    }

    @Override
    public MetricGroupDTO create(final MetricGroupDTO dto) {
        final MetricGroup model = this.metricGroupConverter.toModel(dto);
        this.metricGroupRepository.save(model);
        return this.metricGroupConverter.toDTO(model);
    }

    @Override
    public MetricGroupDTO update(String code, final MetricGroupDTO dto) {
        MetricGroup model = this.metricGroupRepository.findByCode(code);
        if (null == model) {
            throw new CustomRuntimeException(DatabusErrorCode.ERR_CODE_404001, new Object[] {code });
        }
        dto.setId(model.getId());
        dto.setUpdateTime(new Date());
        model = this.metricGroupConverter.toModel(dto);
        this.metricGroupRepository.save(model);
        return this.metricGroupConverter.toDTO(model);
    }

    @Override
    public void deleteById(final Long id) {
        final MetricGroup model = this.metricGroupRepository.getOne(id);
        if (null == model) {
            throw new CustomRuntimeException(DatabusErrorCode.ERR_CODE_404001, new Object[] {id });
        }
        this.metricGroupRepository.delete(model);
    }

    @Override
    public void deleteByCode(final String code) {

        final MetricGroup model = this.metricGroupRepository.findByCode(code);
        if (null == model) {
            throw new CustomRuntimeException(DatabusErrorCode.ERR_CODE_404001, new Object[] {code });
        }
        this.metricGroupRepository.delete(model);
    }

    @Override
    public void bindById(final Long id, final List<Long> metricIdList) {
        final MetricGroup model = this.metricGroupRepository.getOne(id);
        if (null == model) {
            throw new CustomRuntimeException(DatabusErrorCode.ERR_CODE_404001, new Object[] {id });
        }
        final Map<String, String> oldMetrics = new HashMap<>();
        model.getMetrics().forEach(metric -> {
            oldMetrics.put(metric.getCode(), metric.getName());
        });
        model.getMetrics().clear();
        metricIdList.stream().map(i -> {
            final Metric metric = this.metricRepository.getOne(i);
            return metric;
        }).filter(m -> m != null).forEach(model.getMetrics()::add);
        model.setUpdateTime(new Date());
        this.metricGroupRepository.save(model);
    }

    @Override
    public MetricGroupDTO bindByCode(final String code, final List<String> metricCodeList) {
        final MetricGroup model = this.metricGroupRepository.findByCode(code);
        if (null == model) {
            throw new CustomRuntimeException(DatabusErrorCode.ERR_CODE_404001, new Object[] {code });
        }
        final Map<String, String> oldMetrics = new HashMap<>();
        model.getMetrics().forEach(metric -> {
            oldMetrics.put(metric.getCode(), metric.getName());
        });
        model.getMetrics().clear();
        metricCodeList.stream().map(c -> {
            final Metric metric = this.metricRepository.findByCode(c);
            return metric;
        }).filter(m -> m != null).forEach(model.getMetrics()::add);
        model.setUpdateTime(new Date());
        this.metricGroupRepository.save(model);

        return this.metricGroupConverter.toDTO(model);
    }

    @Override
    public void export(Searchable searchable, Pageable pageable, HttpServletResponse response) {
        final StringBuffer fileName = new StringBuffer("指标组");
        fileName.append(DatabusCoreConstant.EXPORT_SPLITOR)
                .append(DateUtil.currentTimestamp2String(DateUtil.PATTERN_NULL_CONN));
        ExcelBoot
                .ExportBuilder(response, fileName.toString(), MetricGroupPortVO.class)
                .exportMultiSheetResponse(searchable, new ExportFunction<Searchable, MetricGroup>() {
                    @Override
                    public List<MetricGroup> pageQuery(final Searchable searchable, final int pageNum,
                            final int pageSize) {
                        final QPageRequest qPageRequest = new QPageRequest(pageNum - 1, pageSize);
                        final Page<MetricGroup> models =
                                MetricGroupServiceImpl.this.metricGroupRepository.search(qPageRequest, searchable);
                        return models.getContent();
                    }

                    @Override
                    public MetricGroupPortVO convert(final MetricGroup metricGroup) {
                        return MetricGroupServiceImpl.this.metricGroupConverter.toVO(metricGroup);
                    }
                });
    }

}
